﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xejen.Database.Entities;
using SqlSugar;

namespace Xejen.Database.SqlSugar
{
    /// <summary>
    /// 通过SqlSugar技术进行仓储访问
    /// </summary>
    /// <typeparam name="TDbContext">表示数据库上下文对象<see cref="SqlSugarClient"/></typeparam>
    /// <typeparam name="TEntity">表示要访问哪张表的实体映射</typeparam>
    /// <typeparam name="TPrimaryKey">表示要访问的表的主键Id的数据类型</typeparam>
    public class SqlSugarRepository<TDbContext, TEntity, TPrimaryKey> : RepositoryBase<TEntity, TPrimaryKey>, IRepositoryWithDbContext
        where TEntity : class, IEntity<TPrimaryKey>, new()
        where TPrimaryKey : struct
        where TDbContext : SqlSugarClient
    {
        private bool _disposed;

        /// <inheritdoc cref="IDbContextProvider{TDbContext}.DbContext"/>
        public virtual SqlSugarClient Context => _dbContextProvider.GetDbContext();

        private readonly IDbContextProvider<TDbContext> _dbContextProvider;

        /// <summary>
        /// <inheritdoc cref="SqlSugarRepository{TEntity, TPrimaryKey}" path="/summary"/>
        /// </summary>
        /// <param name="dbContextProvider"><inheritdoc cref="IDbContextProvider{TDbContext}" path="/summary"/></param>
        public SqlSugarRepository(IDbContextProvider<TDbContext> dbContextProvider)
        {
            _dbContextProvider = dbContextProvider;
        }

        /// <inheritdoc/>
        public override ISugarQueryable<TEntity> GetAll()
        {
            return Context.Queryable<TEntity>();
        }

        /// <inheritdoc/>
        public override Task<ISugarQueryable<TEntity>> GetAllAsync()
        {
            return Task.FromResult(Context.Queryable<TEntity>());
        }

        /// <inheritdoc/>
        public override ISugarQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
        {
            if (propertySelectors.IsNullOrEmpty())
            {
                return GetAll();
            }

            var query = GetAll();

            foreach (var propertySelector in propertySelectors)
            {
                query = query.IncludeFullJoin(propertySelector);
            }

            return query;
        }

        /// <inheritdoc/>
        public override async Task<List<TEntity>> GetAllListAsync()
        {
            var query = await GetAllAsync();
            return await query.ToListAsync(CancellationToken.None);
        }

        /// <inheritdoc/>
        public override async Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var query = await GetAllAsync();
            return await query.Where(predicate).ToListAsync(CancellationToken.None);
        }

        /// <inheritdoc/>
        public override async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var query = await GetAllAsync();
            return await query.SingleAsync(predicate);
        }

        /// <inheritdoc/>
        public override async Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id)
        {
            var query = await GetAllAsync();
            return await query.FirstAsync(CreateEqualityExpressionForId(id), CancellationToken.None);
        }

        /// <inheritdoc/>
        public override async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var query = await GetAllAsync();
            return await query.FirstAsync(predicate, CancellationToken.None);
        }

        /// <inheritdoc/>
        public override TEntity Insert(TEntity entity)
        {
            entity = Context.Insertable<TEntity>(entity).ExecuteReturnEntity();
            return entity;
        }

        /// <inheritdoc/>
        public override async Task<TEntity> InsertAsync(TEntity entity)
        {
            entity = await Context.Insertable<TEntity>(entity).ExecuteReturnEntityAsync();
            return entity;
        }

        /// <inheritdoc/>
        public override TPrimaryKey InsertAndGetId(TEntity entity)
        {
            entity = Insert(entity);

            return entity.Id;
        }

        /// <inheritdoc/>
        public override async Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity)
        {
            entity = await InsertAsync(entity);

            return entity.Id;
        }

        /// <inheritdoc/>
        public override TPrimaryKey InsertOrUpdateAndGetId(TEntity entity)
        {
            entity = InsertOrUpdate(entity);

            return entity.Id;
        }

        /// <inheritdoc/>
        public override async Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity)
        {
            entity = await InsertOrUpdateAsync(entity);

            return entity.Id;
        }

        /// <inheritdoc/>
        public override TEntity Update(TEntity entity)
        {
            Context.Updateable(entity).ExecuteCommand();
            return entity;
        }

        /// <inheritdoc/>
        public override async Task<TEntity> UpdateAsync(TEntity entity)
        {
            await Context.Updateable(entity).ExecuteCommandAsync();
            return entity;
        }

        /// <inheritdoc/>
        public override void Delete(TEntity entity)
        {
            Context.Deleteable(entity).ExecuteCommand();
        }

        /// <inheritdoc/>
        public override void Delete(TPrimaryKey id)
        {
            Context.Deleteable<TEntity>().In(id).ExecuteCommand();
        }

        /// <inheritdoc/>
        public override async Task<int> CountAsync()
        {
            var query = await GetAllAsync();
            return await query.CountAsync(CancellationToken.None);
        }

        /// <inheritdoc/>
        public override async Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var query = await GetAllAsync();
            return await query.Where(predicate).CountAsync(CancellationToken.None);
        }

        /// <inheritdoc/>
        public override async Task<long> LongCountAsync()
        {
            var query = await GetAllAsync();
            return await query.CountAsync(CancellationToken.None);
        }

        /// <inheritdoc/>
        public override async Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var query = await GetAllAsync();
            return await query.Where(predicate).CountAsync(CancellationToken.None);
        }

        /// <inheritdoc/>
        public SqlSugarClient GetDbContext()
        {
            return Context;
        }

        /// <inheritdoc/>
        protected override void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                Context.Dispose();

                _disposed = true;
            }

            base.Dispose(disposing);
        }
    }
}
